package com.tools.util;

import com.tools.entity.HttpHeader;
import com.tools.entity.HttpParam;
import com.tools.entity.HttpResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.*;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;


/**
 * 作者：赵强
 * 时间：20180218
 * 邮箱：surpass_yourself@163.com
 * 访问HttpClient接口工具类
 * 基于HttpClient4.5开发，默认信任所有证书
 * 目前不支持校验Https证书。
 * 依赖于slf4j.jar、HttpClient4.5.jar、HttpClientCore4.4.jar
 */
public class HttpUtils {
	private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);
	/**
	 * HttpClient线程管理器，在内存中创建唯一存在的一个
	 * 当其它线程内有与当前连接相同的HttpClient对像则使用同一个HttpClient。
	 */
	private static PoolingHttpClientConnectionManager connMgr;
	/**
	 * Requset配置项，配置超时间等
	 */
	private static RequestConfig requestConfig;
	/**
	 * 超时时间
	 */
	private static final int MAX_TIMEOUT = 120*1000;
	private static final String EMPTY="";
	/**
	 * 默认编码
	 */
	public static final String DEFAULT_ENCODING="utf-8";
	static {
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", PlainConnectionSocketFactory.INSTANCE)
				.register("https", createSSLConnSocketFactory(null,null))
				.build();
		// 设置连接池
		connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
		// 设置连接池大小
		connMgr.setMaxTotal(400);
		//设置每个池中每个主机最大链接数
		connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());

		RequestConfig.Builder configBuilder = RequestConfig.custom();
		// 设置连接超时
		configBuilder.setConnectTimeout(MAX_TIMEOUT);
		// 设置读取超时
		configBuilder.setSocketTimeout(MAX_TIMEOUT);
		// 设置从连接池获取连接实例的超时
		configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);
		// 在提交请求之前 测试连接是否可用

		requestConfig = configBuilder.build();
	}

	/**
	 * 发送GET请求
	 * @create 2018/10/18 11:14
	 * @author zhaoqiang9
	 * @param url
	 * @return {@link HttpResult}
	 */
	public static HttpResult doGet(String url) {
		return doGet(url,new HttpHeader());
	}

	/**
	 * 发送GET请求
	 * @create 2018/10/18 11:15
	 * @author zhaoqiang9
	 * @param url
	 * @param headers
	 * @return {@link HttpResult}
	 */
	public static HttpResult doGet(String url,HttpHeader headers){
		return doGet(url,new HttpParam(),headers);
	}

	/**
	 * 发送GET请求
	 * @create 2018/10/18 11:12
	 * @author zhaoqiang9
	 * @param url
	 * @param param
	 * @return {@link HttpResult}
	 */
	public static HttpResult doGet(String url,HttpParam param){
		return doGet(url,param,new HttpHeader());
	}

	/**
	 * 发送GET请求
	 * @create 2018/10/18 11:15
	 * @author zhaoqiang9
	 * @param url
	 * @param params
	 * @param headers
	 * @return {@link HttpResult}
	 */
	public static HttpResult doGet(String url, HttpParam params, HttpHeader headers) {
		String apiUrl = url+params.toGetHttpParam();
		CloseableHttpClient httpClient = HttpClients.custom()
				.setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
		HttpGet httpGet =new HttpGet(apiUrl);
		httpGet.setHeaders(headers.toHeader());
		return execute(httpClient,httpGet);
	}

	/**
	 * 发送POST请求
	 * @create 2018/10/18 11:14
	 * @author zhaoqiang9
	 * @param apiUrl
	 * @return {@link HttpResult}
	 */
	public static HttpResult doPost(String apiUrl) {
		return doPost(apiUrl,EMPTY);
	}

	/**
	 * 发送POST请求
	 * @create 2018/10/18 11:13
	 * @author zhaoqiang9
	 * @param apiUrl
	 * @param header
	 * @return {@link HttpResult}
	 */
	public static HttpResult doPost(String apiUrl,HttpHeader header) {
		return doPost(apiUrl,EMPTY,header);
	}

	/**
	 * 发送POST请求
	 * @create 2018/10/18 11:14
	 * @author zhaoqiang9
	 * @param apiUrl
	 * @param text
	 * @return {@link HttpResult}
	 */
	public static HttpResult doPost(String apiUrl, String text) {
		return doPost(apiUrl,text,new HttpHeader());
	}
	/**
	 * 发送POST请求
	 * @create 2018/10/18 11:14
	 * @author zhaoqiang9
	 * @param apiUrl
	 * @param text
	 * @param headers
	 * @return {@link HttpResult}
	 */
	public static HttpResult doPost(String apiUrl, String text,HttpHeader headers) {
		return doPost(apiUrl,createStringEntity(text),headers.toHeader());
	}

	/**
	 * 发送post请求
	 * @param apiUrl 请求地址
	 * @param params 请求参数
	 * @return
	 */
	public static HttpResult doPost(String apiUrl, HttpParam params) {
		return doPost(apiUrl, params,new HttpHeader());
	}

	/**
	 * 发送post请求
	 * @param apiUrl 请求地址
	 * @param httpParam 请求参数
	 * @param headers 请求头
	 * @return
	 */
	public static HttpResult doPost(String apiUrl, HttpParam httpParam, HttpHeader headers) {
		StringEntity stringEntity = createStringEntity(httpParam.toPostHttpParam());
		return doPost(apiUrl, stringEntity,headers.toHeader());
	}

	/**
	 * 发送送请求
	 * @param url
	 * @param etity
     * @return
     */
	private static HttpResult doPost(String url, StringEntity etity,Header... headers){
		CloseableHttpClient httpClient = HttpClients.custom()
				.setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
		HttpPost httpPost = new HttpPost(url);
		httpPost.setEntity(etity);
		httpPost.setHeaders(headers);
		return execute(httpClient,httpPost);
	}
	/**
	 * 创建SSL安全连接
	 * 判断如果证书不存在则默认信任所有证书
	 * @return
	 */
	private static SSLConnectionSocketFactory createSSLConnSocketFactory(String keyStorePath,String keyStorePassword) {
		SSLConnectionSocketFactory sslsf = null;
		try {
			KeyStore keyStore = createKeyStore(keyStorePath,keyStorePassword);
			if(keyStore==null){
				SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(keyStore, new TrustStrategyImpl()).build();
				sslsf = new SSLConnectionSocketFactory(sslContext);
			}else {
				SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(keyStore, new TrustSelfSignedStrategy()).build();
				sslsf = new SSLConnectionSocketFactory(sslContext);
			}
		} catch (GeneralSecurityException e) {
			LOGGER.error("SSLConnectionSocketFactory GeneralSecurityException",e);
		} catch (IOException e) {
			LOGGER.error("SSLConnectionSocketFactory IOException",e);
		}
		return sslsf;
	}

	/**
	 * 获取安全证书
	 * 此方法为扩展方法
	 * @param path  证书路径
	 * @param password  证书密码
	 * @return
	 * @throws KeyStoreException
	 * @throws IOException
	 * @throws CertificateException
	 * @throws NoSuchAlgorithmException
	 */
	private static KeyStore createKeyStore(String path,String password) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
		KeyStore trustStore=null;
		if(StringUtils.isEmpty(path)){
			return trustStore;
		}
		trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
		FileInputStream fileInputStream = new FileInputStream(new File(path));
		trustStore.load(fileInputStream,password.toCharArray());
		return trustStore;
	}

	/**
	 * 执行连接获取访问结果
	 * @param httpClient   //client对象
	 * @param base  //访问方式
     * @return
     */
	private static HttpResult execute(CloseableHttpClient httpClient,HttpRequestBase base){
		int statusCode;
		String resultStr="";
		try {
			CloseableHttpResponse response = httpClient.execute(base);
			statusCode = response.getStatusLine().getStatusCode();
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				resultStr = EntityUtils.toString(entity, DEFAULT_ENCODING);
			}
		} catch (ParseException e) {
			e.printStackTrace();
			statusCode=HttpResult.Status._EXP;
			resultStr="Service ParseException:"+e.getMessage();
		} catch (IOException e) {
			e.printStackTrace();
			statusCode=HttpResult.Status._EXP;
			resultStr="Service IOException:"+e.getMessage();
		}
		return new HttpResult(statusCode,resultStr);
	}

	/**
	 * 解决中文乱码
	 * @create 2018/10/18 11:16
	 * @author zhaoqiang9
	 * @param paramList
	 * @return {@link StringEntity}
	 */
	private static StringEntity createStringEntity(List<NameValuePair> paramList){
		StringEntity entity= new UrlEncodedFormEntity(paramList, Charset.forName(DEFAULT_ENCODING));
		entity.setContentEncoding(DEFAULT_ENCODING);
		return entity;
	}

	/**
	 * 解决中文乱码
	 * @create 2018/10/18 11:16
	 * @author zhaoqiang9
	 * @param text
	 * @return {@link StringEntity}
	 */
	private static StringEntity createStringEntity(String text){
		StringEntity entity= new StringEntity(StringUtils.defaultString(text), ContentType.DEFAULT_TEXT);
		entity.setContentEncoding(DEFAULT_ENCODING);
		return entity;
	}


	/**
	 * 请求校验实现类，目前不校验
	 * @create 2018/10/18 11:16
	 * @author zhaoqiang9
	 * @return {@link }
	 */
	private static class TrustStrategyImpl implements TrustStrategy{

		@Override
		public boolean isTrusted(X509Certificate[] chain, String authType)
				throws CertificateException {
			// TODO Auto-generated method stub
			return true;
		}
	}
}